﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Microsoft.Forums.OfflineStore;
using Microsoft.Forums.Tests.HelperTools;
using Microsoft.Forums.Tests.HelperTools.Stubs;
using Microsoft.Forums.ForumsClient.Models;
using System.Windows.Threading;
using System.Threading;
using System.IO;
using System.ComponentModel;
using Microsoft.Forums.ForumsClient;

namespace ForumsClientUnitTests
{
    /// <summary>
    /// Test the ThreadedForumModelTests class
    /// </summary>
    [TestFixture]
    public class ThreadedForumModelTests
    {

        // Stubs
        ConnectionStub connectionStub;
        SyncControllerStub syncStub;
        ForumsProviderStub providerStub;
        SubscribedForumsCollection subscribedForums;

        // Frame to use with dispatcher
        DispatcherFrame frame;

        /// <summary>
        /// Initialized the database
        /// </summary>
        [TestFixtureSetUp]
        public void SetupFixture()
        {
            DatabaseHelper.SetupDatabase();
        }

        /// <summary>
        /// Setup stubs and datamodel before each test
        /// </summary>
        [SetUp]
        public void Setup()
        {
            connectionStub = new ConnectionStub();
            syncStub = new SyncControllerStub();
            subscribedForums = new SubscribedForumsCollection();
            SynchronizationManager manager = new SynchronizationManager(syncStub);
            providerStub = new ForumsProviderStub();

            AppDataModel.Initialize(providerStub, manager, connectionStub, DatabaseHelper.DatabaseFileName, subscribedForums);
        }

        /// <summary>
        /// After test is complete remove all data we added from the database
        /// 
        /// Ideally, we would just be able to recreate the database but there is an odd 
        /// file locked issue that happens intermittently
        /// </summary
        [TearDown]
        public void Teardown()
        {
            DatabaseHelper.ClearDatabase();
        }

        /// <summary>
        /// Stop the execution of the current dispatcher frame
        /// 
        /// This is used in testing functionality which relies on a running dispatcher
        /// When the test finishes we need EndDispatcher to be asynchronously called to end a previous call to
        /// StartDispatcherFrame
        /// </summary>
        private void StopDispatcherFrame(object container, EventArgs e)
        {
            frame.Continue = false;
        }

        /// <summary>
        /// Starts a dispatcher frame running by setting the frame object's 
        /// continue property to true and then push the frame onto the current dispatcher
        /// </summary>
        private void StartDispatcherFrame()
        {
            frame = new DispatcherFrame();
            Dispatcher.PushFrame(frame);
        }


        #region Constructor Test

        /// <summary>
        /// Test that an exception is thrown if a null forum is passed into the constructor
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ConstructorThrowsIfForumIsNull()
        {
            new ThreadedForumViewModel(null, Dispatcher.CurrentDispatcher);
        }

        /// <summary>
        /// Test the constructor will set the public Forum property and 
        /// creates an empty collection for RootPosts
        /// </summary>
        [Test]
        public void ConstructorSetPublicProperties()
        {
            var forum = new Forum { Id = Guid.NewGuid()};
            forum.ForumPropertySet = new ForumPropertySet();

            var forumModel = new ThreadedForumViewModel(forum, Dispatcher.CurrentDispatcher);
            Assert.AreEqual(0, forumModel.Posts.Count);
        }

        #endregion

        /// <summary>
        /// Test that accessing RootPosts will add correct posts to the RootPosts collection
        /// </summary>
        [Test]
        public void AccessRootPostsPopulatesCollection()
        {
            ForumDataCreator creator = new ForumDataCreator(DatabaseHelper.GetNewDataContext());
            creator.Load("RootPostTest.xml");
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Forum forum = db.Forums.Single(f=>f.Name == "Forum1");
            ThreadedForumViewModel container = new ThreadedForumViewModel(forum, Dispatcher.CurrentDispatcher);
            container.PropertyChanged +=
                delegate(object sender, PropertyChangedEventArgs e)
                {
                    if (e.PropertyName.Equals("AreRootPostsLoaded", StringComparison.InvariantCulture))
                    {
                        StopDispatcherFrame(sender, e);
                    }
                };
            var rootCount = container.Posts.Count();
            Assert.AreEqual(0, rootCount);
            StartDispatcherFrame();
            
            Assert.AreEqual(3, container.Posts.Count);
            foreach (ThreadedPostViewModel post in container.Posts)
            {
                Assert.IsTrue(db.Posts.Any(p => p == post.Post));
            }

        }

    }
}
